/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	GraphicsPipeline.h
*
*	Description -	Manager for managing different stages of the graphics pipeline.
*
*	Comments	-
*
*	Modification History:
*			Name			   Date					Description
*			MrCodeSushi	-	10/19/2011	-	Creation of this file
**************************************************************************************/
#ifndef _PSX_GRAPHICS_PIPELINE_H_
#define _PSX_GRAPHICS_PIPELINE_H_

#include "PulseSTD.h"
#include "GraphicsTypes.h"
#include "InputAssemblerStage.h"
#include "VertexShaderStage.h"
#include "PixelShaderStage.h"
#include "RasterizerStage.h"
#include "OutputMergerStage.h"
#include "ComputeShaderStage.h"

namespace Pulse
{

	class GraphicsPipeline
	{
	public:

		GraphicsPipeline( Renderer *pRenderer );

		~GraphicsPipeline( void );

		// Setup stages
		void SetInputAssemblerStage( InputAssemblerStage *pStage );
		void SetVertexShaderStage( VertexShaderStage *pStage );
		void SetPixelShaderStage( PixelShaderStage *pStage );
		void SetRasterizerStage( RasterizerStage *pStage );
		void SetOutputMergerStage( OutputMergerStage *pStage );
		void SetComputeShaderStage( ComputeShaderStage *pStage );

		InputAssemblerStage *	GetInputAssemblerStage( void )	{ return m_pInputAssembler; }
		VertexShaderStage	*	GetVertexShaderStage( void )	{ return m_pVertexShader; }
		PixelShaderStage	*	GetPixelShaderStage( void )		{ return m_pPixelShader; }
		RasterizerStage		*	GetRasterizerStage( void )		{ return m_pRasterizer; }
		OutputMergerStage	*	GetOutputMergerStage( void )	{ return m_pOutputMerger; }
		ComputeShaderStage	*	GetComputeShaderStage( void )	{ return m_pComputeShader; }

		// Input Assembler
		void IASetInputLayout( InputLayout *pLayout );
		void IASetTopology( ETopology::Type topology );
		void IASetVertexBuffer( UINT slot, VertexBuffer *pVB, UINT offset = 0 );
		void IASetIndexBuffer( IndexBuffer *pIB, UINT offset = 0 );
		void IAClearStates( void );

		// Vertex Shader
		void VSSetShader( Shader *pShader );
		void VSSetConstantBuffer( UINT slot, ConstantBuffer *pConstantBuffer );
		void VSSetSamplerState( UINT slot, SamplerState *pSampler );
		void VSSetShaderResource( UINT slot, ShaderResource *pGraphicsResource );
		void VSClearStates( void );

		// Pixel Shader
		void PSSetShader( Shader *pShader );
		void PSSetConstantBuffer( UINT slot, ConstantBuffer *pConstantBuffer );
		void PSSetSamplerState( UINT slot, SamplerState *pSampler );
		void PSSetShaderResource( UINT slot, ShaderResource *pGraphicsResource );
		void PSClearStates( void );

		// Rasterizer Stage
		void RSSetState( RasterizerState *pState );
		void RSSetScissorRect( Rect *pRect );
		void RSSetViewport( Viewport *pViewport );
		void RSClearStates( void );

		// Output Merger
		void OMSetBlendState( BlendState *pBlendState );
		void OMSetBlendFactors( FLOAT r, FLOAT g, FLOAT b, FLOAT a );
		void OMSetBlendFactors( const FLOAT *pBlendFactors );
		void OMSetSampleMask( const UINT sampleMask );
		void OMSetDepthStencilState( DepthStencilState *pDepthStencilState );
		void OMSetStencilRef( UINT ref );
		void OMSetDepthStencil( ShaderResource *pRT );
		void OMSetRenderTarget( const UINT slot, ShaderResource *pRT );
		void OMSetRenderTarget( const UINT numTargets, ShaderResource **pRTS );
		void OMClearStates( void );
		void OMClearRenderBuffers( const Color *pClearColor, FLOAT depth = 1.0f, UINT stencil = 0 );

		// Compute Stage
		void CSSetShader( Shader *pShader );
		void CSSetConstantBuffer( UINT slot, ConstantBuffer *pConstantBuffer );
		void CSSetShaderResource( UINT slot, ShaderResource *pGraphicsResource );
		void CSSetUnorderedAcces( UINT slot, ShaderResource *pGraphicsResource );
		void CSClearStates( void );
		void CSBindStates( void );

		// Utility methods for setting resources to shader stages
		void SetConstantBuffer( EShader::Type shaderStage, UINT slot, const ConstantBuffer *pConstantBuffer );
		void SetSamplerState( EShader::Type shaderStage, UINT slot, const SamplerState *pSampler );
		void SetShaderResource( EShader::Type shaderStage, UINT slot, const ShaderResource *pGraphicsResource );
		void SetUnorderedAccessResource( EShader::Type shaderStage, UINT slot, const ShaderResource *pGraphicsResource );
		
		// Pipeline executions
		void ClearStageStates( const BOOL bIA, const BOOL bVS, const BOOL bPS, const BOOL bRS, const BOOL bOM, const BOOL bCS );
		void ApplyStageStates( const BOOL bIA, const BOOL bVS, const BOOL bPS, const BOOL bRS, const BOOL bOM, const BOOL bCS );

		// Draw methods
		void DrawIndexed( UINT indexCount, UINT startIndexLocation = 0, INT baseVertexLocation = 0 );

		// Compute Shader methods
		void Dispatch( UINT threadCountX, const UINT threadCountY, const UINT threadCountZ );

	private:

		void BindStateChanges( void );

	private:

		void SetStageRenderer( IPipelineStage *pStage );

		Renderer *m_pRenderer;

		union
		{
			struct
			{
				InputAssemblerStage *m_pInputAssembler;
				VertexShaderStage	*m_pVertexShader;
				HullShaderStage		*m_pHullShader;
				TesellatorStage		*m_pTesellator;
				DomainShaderStage	*m_pDomainShader;
				GeometryShaderStage	*m_pGeometryShader;
				StreamOutputStage	*m_pStreamOutput;
				RasterizerStage		*m_pRasterizer;
				PixelShaderStage	*m_pPixelShader;
				OutputMergerStage	*m_pOutputMerger;
				ComputeShaderStage	*m_pComputeShader;
			};

			IPipelineStage *m_pStages[EPipelineStage::EnumCount];
		};

		IShaderStage *m_pShaderStages[EShader::EnumCount];
	};

	PSX_INLINE void GraphicsPipeline::IAClearStates( void )
	{
		m_pInputAssembler->ClearStates();
	}

	PSX_INLINE void GraphicsPipeline::IASetInputLayout( InputLayout *pLayout )
	{
		m_pInputAssembler->SetInputLayout( pLayout );
	}

	PSX_INLINE void GraphicsPipeline::IASetTopology( ETopology::Type topology )
	{
		m_pInputAssembler->SetTopology( topology );
	}

	PSX_INLINE void GraphicsPipeline::IASetVertexBuffer( UINT slot, VertexBuffer *pVB, UINT offset )
	{
		m_pInputAssembler->SetVertexBuffer( slot, pVB, offset );
	}

	PSX_INLINE void GraphicsPipeline::IASetIndexBuffer( IndexBuffer *pIB, UINT offset )
	{
		m_pInputAssembler->SetIndexBuffer( pIB, offset );
	}

	PSX_INLINE void GraphicsPipeline::VSSetShader( Shader *pShader )
	{
		m_pVertexShader->SetShader( pShader );
	}

	PSX_INLINE void GraphicsPipeline::VSSetConstantBuffer( UINT slot, ConstantBuffer *pConstantBuffer )
	{
		m_pVertexShader->SetConstantBuffer( slot, pConstantBuffer );
	}

	PSX_INLINE void GraphicsPipeline::VSSetSamplerState( UINT slot, SamplerState *pSampler )
	{
		m_pVertexShader->SetSamplerState( slot, pSampler );
	}

	PSX_INLINE void GraphicsPipeline::VSSetShaderResource( UINT slot, ShaderResource *pGraphicsResource )
	{
		m_pVertexShader->SetShaderResource( slot, pGraphicsResource );
	}

	PSX_INLINE void GraphicsPipeline::PSSetShader( Shader *pShader )
	{
		m_pPixelShader->SetShader( pShader );
	}

	PSX_INLINE void GraphicsPipeline::PSSetConstantBuffer( UINT slot, ConstantBuffer *pConstantBuffer )
	{
		m_pPixelShader->SetConstantBuffer( slot, pConstantBuffer );
	}

	PSX_INLINE void GraphicsPipeline::PSSetSamplerState( UINT slot, SamplerState *pSampler )
	{
		m_pPixelShader->SetSamplerState( slot, pSampler );
	}

	PSX_INLINE void GraphicsPipeline::PSSetShaderResource( UINT slot, ShaderResource *pGraphicsResource )
	{
		m_pPixelShader->SetShaderResource( slot, pGraphicsResource );
	}

	PSX_INLINE void GraphicsPipeline::RSSetState( RasterizerState *pState )
	{
		m_pRasterizer->SetRasterizerState( pState );
	}

	PSX_INLINE void GraphicsPipeline::RSSetScissorRect( Rect *pRect )
	{
		m_pRasterizer->SetScissorRect( pRect );
	}
	
	PSX_INLINE void GraphicsPipeline::RSSetViewport( Viewport *pViewport )
	{
		m_pRasterizer->SetViewport( pViewport );
	}
	
	PSX_INLINE void GraphicsPipeline::RSClearStates( void )
	{
		m_pRasterizer->ClearStates();
	}

	PSX_INLINE void GraphicsPipeline::OMClearStates( void )
	{
		m_pOutputMerger->ClearStates();
	}

	PSX_INLINE void GraphicsPipeline::OMClearRenderBuffers( const Color *pClearColor, FLOAT depth, UINT stencil )
	{
		m_pOutputMerger->ClearBuffers( pClearColor, depth, stencil );
	}

	PSX_INLINE void GraphicsPipeline::OMSetBlendState( BlendState *pBlendState )
	{
		m_pOutputMerger->SetBlendState( pBlendState );
	}

	PSX_INLINE void GraphicsPipeline::OMSetBlendFactors( FLOAT r, FLOAT g, FLOAT b, FLOAT a )
	{
		m_pOutputMerger->SetBlendFactors( r, g, b, a );
	}

	PSX_INLINE void GraphicsPipeline::OMSetBlendFactors( const FLOAT *pBlendFactors )
	{
		m_pOutputMerger->SetBlendFactors( pBlendFactors );
	}

	PSX_INLINE void GraphicsPipeline::OMSetSampleMask( const UINT sampleMask )
	{
		m_pOutputMerger->SetSampleMask( sampleMask );
	}

	PSX_INLINE void GraphicsPipeline::OMSetDepthStencilState( DepthStencilState *pDepthStencilState )
	{
		m_pOutputMerger->SetDepthStencilState( pDepthStencilState );
	}

	PSX_INLINE void GraphicsPipeline::OMSetStencilRef( UINT ref )
	{
		m_pOutputMerger->SetStencilRef( ref );
	}

	PSX_INLINE void GraphicsPipeline::OMSetDepthStencil( ShaderResource *pRT )
	{
		m_pOutputMerger->SetDepthStencil( pRT );
	}

	PSX_INLINE void GraphicsPipeline::OMSetRenderTarget( const UINT slot, ShaderResource *pRT )
	{
		m_pOutputMerger->SetRenderTarget( slot, pRT );
	}

	PSX_INLINE void GraphicsPipeline::OMSetRenderTarget( const UINT numTargets, ShaderResource **pRTS )
	{
		m_pOutputMerger->SetRenderTarget( numTargets, pRTS );
	}

	PSX_INLINE void GraphicsPipeline::CSSetShader( Shader *pShader )
	{
		m_pComputeShader->SetShader( pShader );
	}

	PSX_INLINE void GraphicsPipeline::CSSetConstantBuffer( UINT slot, ConstantBuffer *pConstantBuffer )
	{
		m_pComputeShader->SetConstantBuffer( slot, pConstantBuffer );
	}

	PSX_INLINE void GraphicsPipeline::CSSetShaderResource( UINT slot, ShaderResource *pGraphicsResource )
	{
		m_pComputeShader->SetShaderResource( slot, pGraphicsResource );
	}

	PSX_INLINE void GraphicsPipeline::CSSetUnorderedAcces( UINT slot, ShaderResource *pGraphicsResource )
	{
		m_pComputeShader->SetUnordererdAccess( slot, pGraphicsResource );
	}

	PSX_INLINE void GraphicsPipeline::CSClearStates( void )
	{
		m_pComputeShader->ClearStates();
	}

	PSX_INLINE void GraphicsPipeline::CSBindStates( void )
	{
		m_pComputeShader->BindStates();
	}

	PSX_INLINE void GraphicsPipeline::SetConstantBuffer( EShader::Type shaderStage, UINT slot, const ConstantBuffer *pConstantBuffer )
	{
		m_pShaderStages[shaderStage]->SetConstantBuffer( slot, pConstantBuffer );
	}
	
	PSX_INLINE void GraphicsPipeline::SetSamplerState( EShader::Type shaderStage, UINT slot, const SamplerState *pSampler )
	{
		m_pShaderStages[shaderStage]->SetSamplerState( slot, pSampler );
	}
	
	PSX_INLINE void GraphicsPipeline::SetShaderResource( EShader::Type shaderStage, UINT slot, const ShaderResource *pGraphicsResource )
	{
		m_pShaderStages[shaderStage]->SetShaderResource( slot, pGraphicsResource );
	}

	PSX_INLINE void GraphicsPipeline::SetUnorderedAccessResource( EShader::Type shaderStage, UINT slot, const ShaderResource *pGraphicsResource )
	{
		m_pShaderStages[shaderStage]->SetUnordererdAccess( slot, pGraphicsResource );
	}

}

#endif /* _PSX_GRAPHICS_PIPELINE_H_ */